/*
 * Copyright 2022-2029 heshuixing(https://gitee.com/heshuixing)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.mercury.ec.framework.air.core.client.impl.skyecho.execute;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.mercury.ec.framework.air.core.client.impl.skyecho.dto.SkyechoCommonReqDTO;
import com.mercury.ec.framework.air.core.client.impl.skyecho.dto.SkyechoCommonRespDTO;
import com.mercury.ec.framework.air.core.client.impl.skyecho.util.AESPKCS7PaddingUtil;
import com.mercury.ec.framework.air.core.client.impl.skyecho.util.Base64;
import com.mercury.ec.framework.air.core.client.impl.skyecho.util.RSAProvider;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.digest.DigestUtils;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

import static com.mercury.ec.framework.common.exception.util.ServiceExceptionUtil.exception;
import static com.mercury.ec.module.air.enums.ErrorCodeConstants.AIR_SKYECHO_API_CALL_EXCEPTION;

/**
 * 小太阳接口执行器
 *
 * @author hesx
 */
@Slf4j
public class DefaultSkyechoAirExecute implements SkyechoAirExecute {

    @Override
    public <T> SkyechoCommonRespDTO<T> execute(String url, String account, String key, SkyechoCommonReqDTO request, Class<T> clazz) {
        SkyechoCommonRespDTO res = null;
        String salesId = account;
        String corpId = account;
        String auth = account;
        String encodeKey = getEncodeKey();
        Map<String, Object> params = new HashMap<>();
        long start = System.currentTimeMillis();
        try {
            ObjectMapper mapper = new ObjectMapper().registerModule(new JavaTimeModule());
            params = getParams(salesId, corpId, auth, key, request, encodeKey);
            String result = HttpUtil.post(url + request.getUri(), params);
//            String result = FileUtil.readUtf8String(new File("D:\\sky.json"));
            log.info("SKYECHO接口地址【{}】接口【{}】encodeKey【{}】业务参数【{}】请求【{}】耗时【{}】原始响应【{}】", url, request.getUri(), encodeKey, JSONUtil.toJsonPrettyStr(request), params, System.currentTimeMillis() - start, result);
            if (StrUtil.isNotBlank(result)) {
                //解密
                result = result.replaceAll("\\r", "");
                JSONObject resultJson = JSONUtil.parseObj(result);
                if ("10000".equals(resultJson.get("code"))) {
                    if (StrUtil.isNotBlank(resultJson.getStr("data"))) {
//                        encodeKey = "61edd05ceea14478b5d288b2c540f768";
                        String data = AESPKCS7PaddingUtil.decodeECB(resultJson.getStr("data"), encodeKey);
                        data = data.replaceAll("\\n", "").replaceAll("\\t", "");
                        log.info("SKYECHO接口地址【{}】接口【{}】encodeKey【{}】业务参数【{}】请求【{}】耗时【{}】标准DATA【{}】", url, request.getUri(), encodeKey, JSONUtil.toJsonPrettyStr(request), params, System.currentTimeMillis() - start, data);
                        resultJson.set("data", JSONUtil.parseObj(data));
                        result = resultJson.toJSONString(0);
                        // 忽略未能识别的字段
                        mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
                        JavaType javaType = mapper.getTypeFactory().constructParametrizedType(SkyechoCommonRespDTO.class, SkyechoCommonRespDTO.class, clazz);
                        res = mapper.readValue(result, javaType);
                        res.setCode(0);
                        res.setBody(result);
                        log.info("SKYECHO接口地址【{}】接口【{}】encodeKey【{}】业务参数【{}】请求【{}】耗时【{}】标准响应【{}】", url, request.getUri(), encodeKey, JSONUtil.toJsonPrettyStr(request), params, System.currentTimeMillis() - start, result);
                    }
                } else {
                    res = new SkyechoCommonRespDTO();
                    res.setCode(500);
                    res.setBody(result);
                    res.setMsg(resultJson.getStr("msg"));
                    log.error("SKYECHO接口地址【{}】接口【{}】encodeKey【{}】业务参数【{}】请求【{}】耗时【{}】调用异常【{}】", url, request.getUri(), encodeKey, JSONUtil.toJsonPrettyStr(request), params, System.currentTimeMillis() - start, resultJson.getStr("msg"));
                }
            }
        } catch (Exception e) {
            log.error("SKYECHO接口地址【{}】接口【{}】encodeKey【{}】业务参数【{}】请求【{}】耗时【{}】调用异常", url, request.getUri(), encodeKey, JSONUtil.toJsonPrettyStr(request), params, System.currentTimeMillis() - start, e);
            throw exception(AIR_SKYECHO_API_CALL_EXCEPTION, "", e.getMessage());
        }
        return res;
    }

    /**
     * 获取队机签名串
     *
     * @return
     */
    private String getEncodeKey() {
        return UUID.randomUUID().toString().replaceAll("-", "");//EncodeKey
    }

    /**
     * 获取小太阳接口请求参数
     *
     * @param salesId   中心
     * @param corpId    客户
     * @param auth      授权
     * @param key       密钥
     * @param request   请求参数
     * @param encodeKey 签名串
     * @return
     */
    private Map<String, Object> getParams(String salesId, String corpId, String auth, String key, SkyechoCommonReqDTO request, String encodeKey) {
        /**
         * 2.7.	接口提交参数
         * 提交接口的内容如下：
         * SKYECHO=SKY123
         * Sales_ID=ESL003
         * Corp_ID=ESL003
         * SendTime=20200413095005
         * EncodeKey=qtJfIsODp1cqWnnZ%2bn/JzN/8GZIr99aSyv3zAVRZPJteIs8uIjeSxioRE%2b7Xq4M4FyoEmeU/KEW3sKgJRLy62SRoUHifXIb44itT/TZ2I15l5ZJJTDLAbVs3BP9zqDYds3VSErWhfgf3caopW4WhwkF2ZidsLfM4eAeFisFtuwE=
         * Data=hM/z8s/oNuQZ7g2NkpDyW41cwkGO5F8BXZXexGDMvkpcacHDTc7k0M9mhk7xiBvK
         * Sign=d3a7e4812317423f9d5657d848c0272a2dde90b251c77205f4eca9a9b0aa5c26
         * 注：参数值POST前将 + 替换为 %2b
         *
         */
        String des = "";
        try {
            des = Base64.encode(RSAProvider.encryptPublicKey(encodeKey, key));
        } catch (Exception e) {
            log.error("SKYECHO参数加密异常", e);
        }
        ObjectMapper mapper = new ObjectMapper().registerModule(new JavaTimeModule());
        mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
        String requestJson = "";
        try {
            requestJson = mapper.writeValueAsString(request);
        } catch (Exception e) {
            log.error("SKYECHO参数序列化异常", e);
        }
        String aesData = AESPKCS7PaddingUtil.encodeECB(requestJson, encodeKey);
        Map<String, Object> params = new HashMap<>();
        String sendTime = DateUtil.format(new Date(), "yyyyMMddHHmmss");
        params.put("SKYECH", auth);
        params.put("Sales_ID", salesId);
        params.put("Corp_ID", corpId);
        params.put("SendTime", sendTime);
        params.put("EncodeKey", des);
        params.put("Data", aesData);
        params.put("Sign", getSign(corpId, auth, salesId, encodeKey, aesData, sendTime));
        return params;
    }

    /**
     * 获取签名
     *
     * @param salesId   中心
     * @param corpId    客户
     * @param auth      授权
     * @param encodeKey 签名key
     * @param aesData   加密串
     * @param sendTime  请求时间
     * @return
     */
    private String getSign(String salesId, String corpId, String auth, String encodeKey, String aesData, String sendTime) {
        /**
         * 步骤1. 对所有待签名参数按照此顺序（ Corp_ID+Data加密的业务参数值+EncodeKey原文+SKYECH+Sales_ID+SendTime），拼接成字符串string1：
         * ESL003hM/z8s/oNuQZ7g2NkpDyW41cwkGO5F8BXZXexGDMvkpcacHDTc7k0M9mhk7xiBvK9kjg81a44b2d48a582648c2edcty87pjSKY123ESL00320200413095005
         *
         * 步骤2. 对string1进行SHA256签名，得到 Sign：
         * d3a7e4812317423f9d5657d848c0272a2dde90b251c77205f4eca9a9b0aa5c26
         */
//        String s1 = this.corpID + this.data + this.encodeKey + "SKYECH" + this.salesID + this.sendTime;
        String signData = corpId + aesData + encodeKey + auth + salesId + sendTime;
        return DigestUtils.sha256Hex(signData);
    }
}
